/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ifpe.transaction;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * @author Sostenes
 */
public class TransactionManagerProxy implements InvocationHandler {

    Object realSubject;

    public TransactionManagerProxy(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        TransactionManager txManager =
                TransactionManagerFactory.getInstance().createTransactionManager();

        //System.out.println("Iniciando transação...");
        txManager.begin(isReadOnly(method));
        //System.out.println("Transação: iniciada.");
        try {
            result = method.invoke(realSubject, args);
            txManager.commit();
            //System.out.println("Transação: commit.");
        } catch (InvocationTargetException ex) {
            txManager.rollback();
            //System.out.println("Transação: rollback.");
            throw ex.getCause();
        } finally {
            txManager.end();
            //System.out.println("Transação: finalizada.");
        }

        return result;
    }

    private boolean isReadOnly(Method method) {
        Transaction methodAnnotation = getRealSubjectMethod(method).getAnnotation(Transaction.class);
        Transaction classAnnotation = realSubject.getClass().getAnnotation(Transaction.class);

        if (methodAnnotation != null) {
            return methodAnnotation.readOnly();
        } else if (classAnnotation != null) {
            return classAnnotation.readOnly();
        }
        return true;
    }

    private Method getRealSubjectMethod(Method method) {
        try {
            return realSubject.getClass().getMethod(method.getName(), method.getParameterTypes());
        } catch (NoSuchMethodException ex) {
            throw new TransactionManagerException(ex);
        } catch (SecurityException ex) {
            throw new TransactionManagerException(ex);
        }
    }

    public static Object newInstance(Object realSubject) {
        return Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                new TransactionManagerProxy(realSubject));
    }
}
